////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "SCCompilerCommand.hpp"

#include "SCProgram.hpp"
#include "SCCompiler.hpp"
#include "Exception.hpp"
#include <exception>
#include "TKMacros.h"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace Exceptions;

const bool SCCompilerCommand::Command(const vector<string> & arguments) {
	// Input / Output
	vector<string> sources;
	string map_dir = ".", trg_dir = "";
	// Process options
	bool verbose = false;
	// Copy arguments
	vector<string> args(arguments);
	// Remove command name
	args.erase(args.begin());
	// Arguments processing
	unsigned int to_del;
	while (args.size() > 0) {
		to_del = 1;
		// If help is required
		if ((args[0] == "-h") || (args[0] == "--help")) {
			this->help(arguments[0]);
			return true;
		}
		// Verbose option
		else if ((args[0] == "-v") || (args[0] == "--verbose"))
			verbose = true;
		// Target directory
		else if (((args[0] == "-t") || (args[0] == "--trg_dir")) && (args.size() > 1)) {
			trg_dir = args[1];
			if (trg_dir.empty()) {
				this->error();
				this->help(arguments[0]);
				return false;
			}
			to_del = 2;
		}
		// Map directory
		else if (((args[0] == "-m") || (args[0] == "--map_dir")) && (args.size() > 1)) {
			map_dir = args[1];
			if (map_dir.empty()) {
				this->error();
				this->help(arguments[0]);
				return false;
			}
			to_del = 2;
		}
		// Wrong option
		else if ((args[0].substr(0,1) == "-") || (args[0].substr(0,2) == "--")) {
			this->error("incorrect option or incorrect usage of "+args[0]);
			this->help(arguments[0]);
			return false;
		}
		// Add source
		else sources.push_back(args[0]);
		while (to_del > 0) {
			args.erase(args.begin());
			--to_del;
		}
	}
	// If no source, then error
	if (sources.empty()) {
		this->error();
		this->help(arguments[0]);
		return false;
	}
	// Setup target directory
	if (!trg_dir.empty() &&
		((trg_dir.find_last_of('/') != trg_dir.size()-1) && (trg_dir.find_last_of('\\') != trg_dir.size()-1)))
		trg_dir += "/";
	// Compile
	SCCompiler compiler(ostream);
	try {
		for (unsigned int i=0; i<sources.size(); ++i) {
			SCProgram program;
			// Getting source file exact name
			string src = sources[i];
			if ((src.size() > 3) && (src.substr(src.size()-3,3) == ".sc"))
				src = src.substr(0,src.size()-3);
			// Read program
			compiler.ReadProgram(src+".sc",program,map_dir);
			// Print if required
			if (verbose) program.Print(this->ostream);
			// Getting target file exact name
			string trg = src;
			if (!trg_dir.empty()) {
				int sep_idx = MAX((int)trg.find_last_of('/'),(int)trg.find_last_of('\\'));
				if (sep_idx != (int)string::npos)
					trg = trg.substr(sep_idx+1,trg.size()-sep_idx);
				trg = trg_dir + trg;
			}
			// Write bytecode
			program.WriteByteCode(trg+".bsc");
			// Write C macros in a header file
			compiler.WriteCMacros(trg+"_macros.h");
		}
	}
	catch (Exception & e) {
		ostream << e.Comment() << endl;
		ostream << "Compilation aborted" << endl;
	}	
	catch (exception & e) {
		ostream << e.what() << endl;
		ostream << "Compilation aborted" << endl;
	}
	return true;
}
		
void SCCompilerCommand::help(const string & name) {
	// Add import directories -I "bouse;lfkjsdklj;udoias" -d "target_dir"
	this->ostream << "Usage: " + name + " [options] src [src2 ...]" << endl;
	this->ostream << "Arguments:" << endl;
	this->ostream << "\t src src2 ...         : Source \".sc\" files list" << endl;
	this->ostream << "Options:" << endl;
	this->ostream << "\t -t | --trg_dir <dir> : Target files output directory" << endl;
	this->ostream << "\t -m | --map_dir <dir> : Character code map file directory" << endl;
	this->ostream << "\t -v | --verbose       : Verbose mode" << endl;
	this->ostream << "\t -h | --help          : Display help information" << endl;
}
		
void SCCompilerCommand::error(const string & msg) {
	this->ostream << "Incorrect command use" << (!msg.empty()?": "+msg:"") << endl;
}

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////
